#!/usr/bin/env bash

# Time-stamp: <2010-11-28 12:03:46 Sunday by taoshanwen>

# @file svntag
# @version 1.0
# @author ahei

readonly PROGRAM_NAME="svntag"
readonly PROGRAM_VERSION="1.0"

bin=`dirname "$0"`
bin=`cd "$bin"; pwd`

. "$bin"/common.sh

usage()
{
    code=1
    if [ $# -gt 0 ]; then
        code="$1"
    fi

    if [ "$code" != 0 ]; then
        redirect="1>&2"
    fi

    eval cat "$redirect" << EOF
usage: ${PROGRAM_NAME} [OPTIONS]

This shell script can help you create or merge svn tag.

Options:
    -c  Create or merge svn current tag.
    -r  Create or merge svn release tag.
    -t <TRUNK_DIR>
        The directory of trunk, default is \`trunk'.
    -T <TAGS_DIR>
        The directory of tags, default is \`tags'.
    -i [<INSTALL_DIR>]
        Install this shell script to your machine, INSTALL_DIR default is /usr/bin.
    -n  Do not really execute command, only print command to execute.
    -R [<FILE>]
        Rollback to prev revision, <FILE> default is \`.'.
    -N <TAG_NAME>
        Set tag name manual.
    -q  Quiet, do not write process info to standard output.
    -s  [IS_STOP]
        When execute commands failed, stop execute other commands and exit.
    -B [IS_BACKUP]
        Specify backup tag or not.
    -d  Do not svn up trunk directory.
    -v  Output version info.
    -h  Output this help.
EOF
    
    exit "$code"
}

getRevisionNo()
{
    dir="$1"
    LANG= svn info "${dir}" 2>/dev/null | grep "Revision: " | sed -r "s/^Revision: ([[:digit:]]+)$/\1/g"
}

rollback()
{
    dir="$1"
    isExecute="$2"
    
    executeCommand "svn up ${dir}" "${isExecute}" "$isQuiet" "$isStop"
    executeCommand "svn merge -r HEAD:PREV ${dir}" "${isExecute}" "$isQuiet" "$isStop"
    exit $?
}

trunkDir="trunk"
tagsDir="tags"
isExecute=1
isStop=1
isQuiet=0

while getopts ":hvcri:t:T:nR:N:s:dB:" OPT; do
    case "$OPT" in
        c)
            tagName="current"
            ;;
            
        r)
            tagName="release"
            ;;

        i)
            install "$OPTARG"
            ;;
            
        t)
            trunkDir="${OPTARG}"
            ;;

        T)
            tagsDir="${OPTARG}"
            ;;

        n)
            isExecute=0
            ;;

        R)
            rollbackFile="${OPTARG}"
            ;;

        N)
            tagName="${OPTARG}"
            ;;
            
        q)
            isQuiet=1
            ;;

        s)
            isStop="$OPTARG"
            ;;

        d)
            notSvnup=1
            ;;

        B)
            isBackupTag="$OPTARG"
            ;;
        
        v)
            version
            ;;

        h)
            usage 0
            ;;

        :)
            case "${OPTARG}" in
                s)
                    isStop=1
                    ;;
                
                i)
                    install
                    ;;
                
                R)
                    rollbackFile="."
                    ;;

                B)
                    isBackupTag=1
                    ;;

                ?)
                    echo "Option \`-${OPTARG}' need argument.\n"
                    usage
            esac
            ;;

        ?)
            echo "Invalid option \`-${OPTARG}'.\n"
            usage
            ;;
    esac
done

[ -n "${rollbackFile}" ] && rollback "${rollbackFile}" "${isExecute}"

if [ -z "${tagName}" ]; then
    usage
fi

if [ -f "${tagsDir}/${tagName}" ]; then
    echo "File ${tagsDir}/${tagName} is exists, can not create tag \`${tagName}'."
    exit 1
fi

ecArgs="$isExecute $isQuiet $isStop"

dateTimeRegexp="[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}"

[ "$notSvnup" != 1 ] && executeCommand "svn up ${trunkDir}" $ecArgs

# merge tag
if [ -d "${tagsDir}/${tagName}" ]; then
    executeCommand "svn up ${tagsDir}/${tagName}" $ecArgs

    fromRevisionNo=`svn log "${tagsDir}/${tagName}" -r COMMITTED | sed -n 4p | sed -r "s/^Create tag ${tagName} from trunk r([[:digit:]]+)\.$/\1/g" | grep -x -E "[[:digit:]]+"`
    if [ -z "${fromRevisionNo}" ]; then
        fromRevisionNo=`svn log "${tagsDir}/${tagName}" -r COMMITTED | sed -n 4p | sed -r "s/^Merge trunk r[[:digit:]]+:([[:digit:]]+) to tag ${tagName}\.$/\1/g" | grep -x -E "[[:digit:]]+"`
    fi
    if [ -z "${fromRevisionNo}" ]; then
        executeCommand "svn rm --force ${tagsDir}/$tagName" $ecArgs
        executeCommand "svn ci ${tagsDir}/$tagName -m \"Delete tag $tagName by $PROGRAM_NAME, because can not get revision from it's log.\"" $ecArgs
        createTag=1
    fi

    if [ "$createTag" != 1 ]; then
        if [ "${tagName}" = "release" ]; then
            dateTime=`svn log "${tagsDir}/${tagName}" -r COMMITTED | sed -n 2p | sed -r "s/^r[[:digit:]]+ \| .+ \| (${dateTimeRegexp}).* \| .*$/\1/g" | grep -x -E "${dateTimeRegexp}" | sed "s/ /-/g; s/:/_/g"`
            if [ -z "${dateTime}" ]; then
                echo "Get date time of tag ${tagName} failed."
                exit 1
            fi

            if [[ "$isBackupTag" != 0 ]]; then
                executeCommand "svn cp ${tagsDir}/${tagName} ${tagsDir}/${tagName}_${dateTime}" $ecArgs
            fi
        fi

        toRevisionNo=`getRevisionNo "${trunkDir}"`
        executeCommand "svn merge ${trunkDir} -r${fromRevisionNo}:${toRevisionNo} ${tagsDir}/${tagName}" $ecArgs
        executeCommand "svn ci -m \"Merge trunk r${fromRevisionNo}:${toRevisionNo} to tag ${tagName}.\"" $ecArgs
        
        exit
    fi
fi

# create tag
revisionNo=`getRevisionNo "${trunkDir}"`
if [ -z "${revisionNo}" ]; then
    echo "Get revision number of ${trunkDir} failed."
    exit 1
fi

if [[ ! -e "$tagsDir" ]]; then
    if svn info $tagsDir >/dev/null 2>&1; then
        executeCommand "svn up $tagsDir" "$isExecute" "$isQuiet" "isStop"
    else
        executeCommand "svn mkdir $tagsDir" "$isExecute" "$isQuiet" "isStop"
    fi
fi

executeCommand "svn cp ${trunkDir} ${tagsDir}/${tagName}" $ecArgs
executeCommand "svn ci -m \"Create tag ${tagName} from trunk r${revisionNo}.\"" $ecArgs
